#include <avr/io.h>
#include <util/delay.h>

#include "nexa.h"

#define NEXA_TX_PORT       PORTB
#define NEXA_TX_DDR        DDRB
#define NEXA_TX_PIN        7

#define LED_PORT           PORTD
#define LED_DDR            DDRD
#define LED_PIN            5

#define NEXA_TX_RETRIES    5
#define NEXA_MSG_SIZE      65
#define NEXA_ADR_LENGTH    26
#define NEXA_CFG_LENGTH    6

#define NEXA_HIGH          (NEXA_TX_PORT |=  (1 << NEXA_TX_PIN)); (LED_PORT &= ~(1 << LED_PIN))
#define NEXA_LOW           (NEXA_TX_PORT &= ~(1 << NEXA_TX_PIN)); (LED_PORT |=  (1 << LED_PIN))

#define NEXA_190_DELAY     _delay_ms(0.190)
#define NEXA_370_DELAY     _delay_ms(0.370)
#define NEXA_1350_DELAY    _delay_ms(1.350)
#define NEXA_2700_DELAY    _delay_ms(2.700)
#define NEXA_10000_DELAY   _delay_ms(10.000)

#define NEXA_MARK          NEXA_HIGH; NEXA_190_DELAY; NEXA_LOW; NEXA_1350_DELAY; NEXA_HIGH; NEXA_190_DELAY; NEXA_LOW; NEXA_370_DELAY
#define NEXA_SPACE         NEXA_HIGH; NEXA_190_DELAY; NEXA_LOW; NEXA_370_DELAY;  NEXA_HIGH; NEXA_190_DELAY; NEXA_LOW; NEXA_1350_DELAY


/*
unsigned char NEXA_CMD_ON[NEXA_MSG_SIZE] =  {1,0, 1,0, 1,0, 0,1, 1,0, 0,1, 0,1, 1,0, 0,1, 0,1, 1,0, 1,0, 0,1, 0,1, 0,1, 0,1, 1,0, 1,0, 0,1, 1,0, 0,1, 1,0, 1,0, 1,0, 0,1, 1,0,
                                             1,0,
                                             0,1,
                                             1,0,1,0,
                                             1,0,1,0,
                                             0};

unsigned char NEXA_CMD_OFF[NEXA_MSG_SIZE] = {1,0,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,0,1,0,1,1,0,1,0,0,1,1,0,0,1,1,0,1,0,1,0,0,1,1,0,
                                             1,0,
                                             1,0,
                                             1,0,1,0,
                                             1,0,1,0,
                                             0};
*/

void nexa_init()
{
    NEXA_TX_DDR |= (1 << NEXA_TX_PIN);
    LED_DDR |= (1 << LED_PIN);
    LED_PORT |= (1 << LED_PIN);
}

void nexa_pair()
{
    //nexa_send_cmd(NEXA_CMD_ON);

    //5979298 = 0b00000000010110110011110010100010
    nexa_send_cmd2(5979298, 0, 0, 0, 1);
}

void nexa_on()
{
    //nexa_send_cmd(NEXA_CMD_ON);
    nexa_send_cmd2(5979298, 0, 0, 0, 1);
}

void nexa_off()
{
    //nexa_send_cmd(NEXA_CMD_OFF);
    nexa_send_cmd2(5979298, 0, 0, 0, 0);
}



void nexa_send_cmd(unsigned char* cmd)
{
    for (unsigned char i = 0; i < NEXA_TX_RETRIES; ++i)
    {
        // NEXA start pattern
        NEXA_HIGH;
        NEXA_190_DELAY;
        NEXA_LOW;
        NEXA_2700_DELAY;

        // NEXA data
        for (unsigned char k = 0; k < NEXA_MSG_SIZE; ++k)
        {
            if (cmd[k] == 0)
            {
                NEXA_HIGH;
                NEXA_190_DELAY;
                NEXA_LOW;
                NEXA_1350_DELAY;
            }
            else
            {
                NEXA_HIGH;
                NEXA_190_DELAY;
                NEXA_LOW;
                NEXA_370_DELAY;
            }
        }

        // NEXA intra pause
        NEXA_10000_DELAY;
    }
}

void nexa_send_cmd2(uint32_t address,
                   uint8_t group,
                   uint8_t channel,
                   uint8_t button,
                   uint8_t state)
{

    uint8_t configuration = 0;
    configuration |= ((group   & 0x01) << 5);
    configuration |= ((state   & 0x01) << 4);
    configuration |= ((channel & 0x03) << 2);
    configuration |=  (button  & 0x03);

    for (uint8_t i = 0; i < NEXA_TX_RETRIES; ++i)
    {
        // NEXA start pattern
        NEXA_HIGH;
        NEXA_190_DELAY;
        NEXA_LOW;
        NEXA_2700_DELAY;

        // NEXA data
        for (int8_t j = NEXA_ADR_LENGTH - 1; j >= 0; --j)
        {
            if (((address >> j) & 0x01) == 0)
            {
                NEXA_SPACE;
            }
            else
            {
                NEXA_MARK;
            }
        }

        for (int8_t k = NEXA_CFG_LENGTH - 1; k >= 0; --k)
        {
            if (((configuration >> k) & 0x01) == 0)
            {
                NEXA_SPACE;
            }
            else
            {
                NEXA_MARK;
            }
        }

        // NEXA stop pattern
        NEXA_HIGH;
        NEXA_190_DELAY;
        NEXA_LOW;

        // NEXA intra pause
        NEXA_10000_DELAY;
    }
}
